import Watcher from './Watcher.js'

export default class Compiler {

  constructor(vm) {
    if (vm.$el) {
      const df = Compiler.node2df(vm.$el)
      Compiler.compile(df, vm)
      vm.$el.appendChild(df)
    }
  }

  static node2df(el) {
    const df = document.createDocumentFragment()
    let c = null
    while (c = el.firstChild) {
      df.appendChild(c)
    }
    return df
  }

  static compile(df, vm) {
    const nodes = Array.from(df.childNodes)
    for (const v of nodes) {
      if (v.nodeType === 1) {
        Compiler.compileElement(v, vm)
      } else if (v.nodeType === 3) {
        Compiler.compileText(v, vm)
      }
    }
  }

  static compileElement(node, vm) {
    const attributes = Array.from(node.attributes)
    for (const v of attributes) {
      const {
        name,
        value
      } = v
      if (name.startsWith('v-')) {
        const directiveName = name.substr(2)
        switch (directiveName) {
          case 'model':
            console.log('识别到model指令', name, value);
            node.value = Compiler.getValue(vm.$data, value)
            node.addEventListener('input', e => {
              Compiler.setValue(vm.$data, value, e.target.value)
            })
            new Watcher(vm.$data, value, (newValue) => {
              node.value = newValue
            })
            break
          case 'if':
            console.log('识别到if指令', name, value);
            break
          default:
            break
        }
      }
    }
    Compiler.compile(node, vm)
  }

  static compileText(node, vm) {
    let result = node.textContent.match(/\{\{\s*([\w\.]+)\s*\}\}/g)
    if (result) {
      for (let str of result) {
        const expr = str.slice(2, str.length - 2).trim()
        node.textContent = node.textContent.replace(str, Compiler.getValue(vm.$data, expr))
        new Watcher(vm.$data, expr, (newValue, oldValue) => {
          node.textContent = node.textContent.replace(oldValue, newValue)
        })
      }
    }
  }

  static getValue(obj, expr) {
    const keys = expr.split('.')
    for (const key of keys) {
      obj = obj[key]
    }
    return obj
  }

  static setValue(obj, expr, value) {
    const keys = expr.split('.')
    const lastKey = keys.pop()
    for (const key of keys) {
      obj = obj[key]
    }
    obj[lastKey] = value
  }

}
